/**
 * Copyright 2011 Honeywell Federal Manufacturing & Technologies, LLC. This material was produced under U.S. Government
 * contract DE-ACO4-01AL66850, Honeywell Federal Manufacturing & Technologies, LLC, (FM&T) with the U. S. Department of
 * Energy for the operation of the Kansas City Plant . The U.S. Government has rights to use, reproduce, and distribute
 * this software.  NEITHER THE GOVERNMENT NOR Honeywell Federal Manufacturing & Technologies, LLC, MAKES ANY WARRANTY,
 * EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.  If software is modified to produce
 * derivative works, such modified software should be clearly marked, so as not to confuse it with the version available
 * from FM&T.
 *
 * Additionally, this program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License Version 3 published by the Free Software Foundation. Accordingly, this program is distributed in
 * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this library.
 * If not, see http://www.gnu.org/licenses/.
 */
package org.braintrain.tapestry.services;

import com.kcp.ko.fmtnmlib.hibernate.CriteriaFactory;
import com.kcp.ko.fmtnmlib.tapestry.services.GenericEngineServiceAdapter;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.json.JSONArray;
import org.apache.tapestry.json.JSONLiteral;
import org.apache.tapestry.json.JSONObject;
import org.apache.tapestry.util.ContentType;
import org.apache.tapestry.web.WebResponse;
import org.braintrain.domain.EvaluationData;
import org.braintrain.domain.Module;
import org.braintrain.domain.types.TrainingRating;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Property;

import javax.activation.MimetypesFileTypeMap;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * A service used to generate JSON objects for use in pie graph creation as pertaining to evaluation scores of a module
 * User: Robert H. Engelhardt
 * Date: 4/19/11
 * Time: 2:12 PM
 */
public class EvaluationStatisticsService extends GenericEngineServiceAdapter {

    public static final String SERVICE_NAME = "evaluationStatisticsService";

    private MimetypesFileTypeMap mimeTypes = new MimetypesFileTypeMap();

    public static final String MODULE_ID = "moduleId";
    public static final String START_DATE = "startDate";
    public static final String END_DATE = "endDate";
    public static final String METRIC_NAME = "metric";

    public String getName() {
        return SERVICE_NAME;
    }

    public void service(IRequestCycle cycle) throws IOException {

        WebResponse response = cycle.getInfrastructure().getResponse();

        Long moduleId = unsqueezeParameter(cycle, Long.class, MODULE_ID);
        Module module = getHibernateSearchService().getById(Module.class, moduleId);

        if(module==null) {
            return;
        }

        Date startDate = unsqueezeParameter(cycle, Date.class, START_DATE);
        Date endDate = unsqueezeParameter(cycle, Date.class, END_DATE);
        MetricType metricType = MetricType.fromParameter(unsqueezeParameter(cycle, String.class, METRIC_NAME));

        // initialize trainingRatingValuerMap with 0 count for each trainingRating
        Map<TrainingRating, Integer> trainingRatingValuerMap = new HashMap<TrainingRating, Integer>();
        for(TrainingRating trainingRating: TrainingRating.values()) {
            trainingRatingValuerMap.put(trainingRating, 0);
        }

        // fill out trainingRatingMap based on the gathered evaluationData
        List<EvaluationData> evaluationDataList = getHibernateSearchService().find(new EvaluationDataCriteria(startDate, endDate, module));
        for(EvaluationData evaluationData: evaluationDataList) {
            TrainingRating trainingRating = metricType.getTrainingRating(evaluationData);
            trainingRatingValuerMap.put(trainingRating, trainingRatingValuerMap.get(trainingRating) + 1);
        }

        // generate on output JSON array
        response.setHeader("Cache-Control", "no-cache");
        OutputStream out = response.getOutputStream(new ContentType(mimeTypes.getContentType("application/json")));
        JSONArray jsonArray = new JSONArray();

        for(TrainingRating trainingRating: TrainingRating.values()) {

            JSONObject jsonPair = new JSONObject();
            Integer count = trainingRatingValuerMap.get(trainingRating);

            jsonPair.put(new JSONLiteral("label"), trainingRating.getName() + " (" + count + ")");
            jsonPair.put(new JSONLiteral("data"), count);
            jsonArray.put(jsonPair);

        }

        out.write(jsonArray.toString().getBytes());
    }

    /**
     * Criteria for finding evaluations
     */
    private final class EvaluationDataCriteria implements CriteriaFactory<EvaluationData> {

        private Date startDate;
        private Date endDate;
        private Module module;

        private EvaluationDataCriteria(Date startDate, Date endDate, Module module) {
            this.startDate = startDate;
            this.endDate = endDate;
            this.module = module;
        }

        public Criteria getCriteria(Session session) {
            Criteria criteria = session.createCriteria(EvaluationData.class);
            criteria.add(Property.forName("module").eq(module));
            criteria.add(Property.forName("evalDate").between(startDate, endDate));
            return criteria;
        }
    }

    /**
     * Definition of metric types
     */
    private enum MetricType {

        EXPECTATIONS("expectations") {
            @Override
            public TrainingRating getTrainingRating(EvaluationData evaluationData) {
                return evaluationData.getExpectations();
            }
        },
        QUALITY("quality") {
            @Override
            public TrainingRating getTrainingRating(EvaluationData evaluationData) {
                return evaluationData.getQuality();
            }
        },
        AMOUNT("amount") {
            @Override
            public TrainingRating getTrainingRating(EvaluationData evaluationData) {
                return evaluationData.getAmount();
            }
        },
        JOB_HELP("jobHelp") {
            @Override
            public TrainingRating getTrainingRating(EvaluationData evaluationData) {
                return evaluationData.getJobHelp();
            }
        },
        USER_FRIENDLY("userFriendly") {
            @Override
            public TrainingRating getTrainingRating(EvaluationData evaluationData) {
                return evaluationData.getUserFriendly();
            }
        },
        REACTION("reaction") {
            @Override
            public TrainingRating getTrainingRating(EvaluationData evaluationData) {
                return evaluationData.getReaction();
            }
        };

        private final String parameter;

        private MetricType(String parameter) {
            this.parameter = parameter;
        }

        public String getParameter() {
            return parameter;
        }

        public static MetricType fromParameter(String parameter) {

            for(MetricType metricType: values()) {
                if(metricType.getParameter().equalsIgnoreCase(parameter)) {
                    return metricType;
                }
            }

            return null;
        }

        public abstract TrainingRating getTrainingRating(EvaluationData evaluationData);

    }

}
